
from models import Collection, Annotation, Dataset, ChartView
from blog.models import BlogEntry
import models, users, util

from timeseries import format_time_series
from datetime import datetime
from gviz_api import DataTable
from django.http import HttpResponse
from ragendja.dbutils import get_object_list, get_object_or_404
from django.shortcuts import render_to_response
from google.appengine.ext import db
from google.appengine.api.users import is_current_user_admin

class EmptyCollectionError (BaseException): pass

def page (fn):
	""" Add user object to page data. Not compatible with require_post."""
	def page_with_user (*args,**kwargs):
		user = users.get_current_user()
		is_admin = is_current_user_admin ()
		datasets = Dataset.all ()
		datasets.fetch (100)
		my_views = []
		template_name, vars = fn (*args,**kwargs)
		if user:
			vars["my_views"] = ChartView.get_views (user=user,saved=True)
			vars["my_annotations"] = Annotation.get_annotations (user=user)
		vars["user"] = user
		vars["datasets"] = datasets
		vars["is_admin"] = is_current_user_admin ()
		return render_to_response (template_name, vars)
	return page_with_user

def require_post (vars, error_page):
	""" Fail to another page if we don't get the requisite postvars. 

	Expects the function to return a template_name, template_data tuple. 
	"""
	def decorator (fn):
		def preconditioned_page (r, *args,**kwargs):
			errors = []
			for var in vars:
				if not r.POST.has_key (var):
					errors.append ("Missing required postvar: " + var)
					return error_page (r, prior_errors=errors)
			response = fn(r, *args,**kwargs)
			return response
		return preconditioned_page
	return decorator

@page
def error_page (r, prior_errors=[]):
	return "error.html", {"errors":prior_errors}

@page
def index (r, prior_messages=[]):
	latest_entries = BlogEntry.all ()
	latest_entries.filter ("published =", True)
	latest_entries.fetch (1)
	if len(latest_entries) > 0: latest_entry = latest_entries[0]
	else: latest_entry = None
	try:
		featured_chart_ids = Collection.from_slug("featured_charts").charts
		if len(featured_chart_ids)<1: raise EmptyCollectionError ()
		featured_chart = db.get (featured_chart_ids[0])
	except models.NoSuchCollectionError:
		featured_chart = None
	except EmptyCollectionError:
		featured_chart = None
	page_data = {'latest_entry' : latest_entry, 'messages':prior_messages, 'featured_chart':featured_chart}
	return "index.html", page_data

@page
def create_view_form (r, prior_errors=[]):
	errors = prior_errors
	datasets = get_object_list (Dataset)
	return "create_view.html", {'datasets':datasets, 'errors':errors}

@require_post (["datasets","start","end"], create_view_form)
def create_view (r):
	user = users.get_current_user()
	keys = r.POST.getlist ('datasets')
	all_datasets = get_object_list (Dataset)
	start = datetime.strptime (r.POST["start"], "%Y-%m-%d")
	end = datetime.strptime (r.POST["end"], "%Y-%m-%d")
	datasets = []
	datakeys = []
	for key in keys:
		dataset = db.Key (encoded=key)
		dataset_obj = db.get (dataset)
		if start < dataset_obj.start or end > dataset_obj.end:
			errors = [dataset_obj.name + " is only available from " + dataset_obj.start.strftime("%Y-%m-%d") + " to " + dataset_obj.end.strftime ("%Y-%m-%d")]
			return error_page (r, prior_errors=errors)
		datakeys.append (dataset)
		datasets.append (db.get(dataset))
	new_view = ChartView (datasets=datakeys,author=user,published=False,start_date=start,end_date=end)
	new_view.put ()
	return chart (r, new_view.key ())

@require_post (["view_key", "name"], error_page)
def save_view (r):
	name = r.POST["name"]
	keystr = r.POST["view_key"]
	view = db.get(db.Key(encoded=keystr))
	view.saved = True
	view.name = name
	view.put ()
	return chart (r, view.key())

@require_post (["name","date","shortdesc","fulldesc","dataset_key","chartview_key"], error_page)
def add_annotation (r):
	annotation = Annotation ()
	annotation.title = r.POST["name"]
	annotation.date = datetime.strptime (r.POST["date"], "%Y-%m-%d")
	annotation.short_description = r.POST["shortdesc"]
	annotation.content = r.POST["fulldesc"]
	dataset_key = r.POST["dataset_key"]
	chartview_key = r.POST["chartview_key"]
	target_dataset = db.get(db.Key(encoded=dataset_key))
	annotation.dataset = target_dataset
	chartview = db.get(db.Key(encoded=dataset_key))
	user = users.get_current_user ()
	if not user: return chart (r, chartview_key, prior_errors=["You must log in to post annotations."])
	annotation.author = user
	annotation.published = True
	annotation.put ()
	messages = ["You have added an annotation."]
	return chart (r, chartview_key, prior_messages = messages)

@require_post (["chart","collection"], error_page)
def add_to_collection (r):
	chartkey = r.POST["chart"]
	collkey = r.POST["collection"]
	chartview = db.Key(encoded=chartkey)
	collection = db.get(db.Key(encoded=collkey))
	collection.charts.append (chartview)
	collection.put ()
	messages = ["Added chart to collection " + collection.name]
	return chart (r, chartkey, prior_messages=messages)

@page
def chart (r, key, prior_errors=[], prior_messages=[]):
	if type(key) == str or type(key) == unicode: 
		key_object = db.Key (encoded=key)
		chartview = db.get(key_object)
	else: chartview = db.get (key)
	datasets_used = [db.get(dataset_key) for dataset_key in chartview.datasets]
	annotations = []
	user = users.get_current_user ()
	if is_current_user_admin ():
		admin_collections = Collection.all ()
		admin_collections.filter ("published =", True)
		admin_collections.fetch (100)
	else: admin_collections = None
	if user:
		my_collections = Collection.all ()
		my_collections.filter ('author =', user)
		my_collections.fetch (100)
	else: my_collections = []
	for dataset in datasets_used:
		annotations += Annotation.get_annotations (start_date = chartview.start_date, end_date = chartview.end_date, dataset=dataset)
	page_data = {'errors':prior_errors, 'messages':prior_messages, 
			'key':key, 'datasets_used':datasets_used, 'chartview':chartview,
			'annotations':annotations,'my_collections':my_collections,'admin_collections':admin_collections}
	return "view.html", page_data 

@page
def collections (r):
	collections = Collection.all ()
	collections.filter ("published =", True)
	collections.fetch (100)
	page_data = {'collections':collections}
	return "collections.html", page_data

@page
def collection (r, coll_slug):
	collections = Collection.all ()
	collections.filter ("slug =", coll_slug)
	collections.fetch (1)
	try: collection = collections[0]
	except:
		return "404.html", {}
	page_data = {'collection' : collections[0]}
	return "collection.html", page_data

def query (r):
	user = users.get_current_user ()
	view_id = r.GET["view"]
	chartview = db.get(db.Key(encoded=view_id))
	datasets = [db.get(key) for key in chartview.datasets]
	table_description = [('Date','date')]
	table_order = ["Date"]
	filenames = []
	for i, dataset in enumerate(datasets):
		table_description += [(dataset.name, 'number'), ('title' + str(i), 'string'), ('desc' + str(i), 'string')]
		table_order += [dataset.name, 'title' + str(i), 'desc' + str(i)]
		filenames.append (dataset.filename)
	ts = format_time_series (filenames, chartview.start_date, chartview.end_date, padding=2)

	for i, dataset in enumerate(datasets):
		dataset_index = i*3 + 2
		annotations = Annotation.get_annotations (start_date=chartview.start_date, end_date=chartview.end_date, dataset=dataset)
		for annotation in annotations:
			annotation_index = ts.get_position (annotation.date)
			row = ts.data[annotation_index][:]
			row[0] = annotation.date
			row[dataset_index]=annotation.title
			row[dataset_index+1]=annotation.short_description
			if annotation.date > ts.data[annotation_index][0]:
				ts.data.insert (annotation_index+1, row)
			else: ts.data.insert (annotation_index, row)
	data_table = DataTable (table_description)
	data_table.LoadData (ts.data)
	json = data_table.ToJSonResponse (columns_order=table_order)
	return HttpResponse (json, mimetype='application/json')

def autodetect (r):
	if not is_current_user_admin (): return error_page (r, prior_errors=["This operation requires administrator privileges."])
	messages = util.autodetect ()
	return index (r, prior_messages=messages)
 
